//创建Axios类
function Axios(config) {
    this.defaults = config; //添加defaults默认属性
    this.interceptors = { //添加拦截器属性
        request: new InterceptorManager(), //添加管理存储拦截器中方法的实例
        response: new InterceptorManager()
    }
}


//拦截器管理类
function InterceptorManager() {
    this.handlers = [];
}

InterceptorManager.prototype.use = function (fullfilled, rejected) {
    this.handlers.push({
        fullfilled,
        rejected
    })
}

//添加实例方法
Axios.prototype.request = function (config) { //创建统一请求函数
    //1、发送前合并defaults和传入的配置参数,此处省略

    //使用promise来处理配置参数,封装为promise类型
    let promise = Promise.resolve(config);

    //设置.then的两个回调函数
    let chains = [dispatchRequest, undefined]; //undefined占位,避免原型链调用出错

    //处理拦截器,因为创建实例时已经将this指向了axios实例,所以能够获取到axios实例上存储的拦截器方法
    //请求拦截器
    this.interceptors.request.handlers.forEach((item) => {
        chains.unshift(item.rejected); //注意压入顺序,先压入失败回调,再压入成功,使得shift()先取出成功回调
        chains.unshift(item.fullfilled);

    })
    //响应拦截器
    this.interceptors.response.handlers.forEach((item) => {
        chains.push(item.fullfilled);
        chains.push(item.rejected);
    })


    while (chains.length) { //遍历存储的方法,执行请求拦截器->发送请求方法dispatchRequest->响应拦截器
        //请求拦截器中的方法会根据返回参数,使用promise成功回调,将每次修改后的配置传递到dispatchRequest中
        //响应拦截器会根据dispatchRequest返回的请求数据,将数据传递给之后的响应拦截器中
        //若中途产生了失败回调,会异常穿透到最后一个promise中
        //最终返回带有结果的promise
        promise = promise.then(chains.shift(), chains.shift()); //shift弹出数组第一个元素,并改变数组结构
    }

    //返回值对应实例返回的Promise对象即最终的数据结果
    return promise;

}

//dispatchRequest函数搭建
function dispatchRequest(config) {
    //调用适配器发送请求
    return xhrAdaptor(config).then(res => { //根据适配器函数的Promise执行器结果,设置.then的Promise的返回值
        return res;
    }, err => {

        throw err;
    })
}

//adaptor适配器函数搭建,这里只实现xhr(前台)环境的情况
function xhrAdaptor(config) {
    return new Promise((resolve, reject) => {
        //进行xhr请求,将结果使用执行器执行,则dispatchRequest返回的Promise内容就是执行器执行的内容,request中收到的Promise内容被dispatchRequest返回的Promise内容决定
        let xhr = new XMLHttpRequest();
        xhr.open(config.method, config.url);
        xhr.send();
        xhr.onreadystatechange = function () {
            if (xhr.readyState === 4) {
                if (xhr.status >= 200 && xhr.status < 300) {
                    resolve({
                        config,
                        data: xhr.response,
                        header: xhr.getAllResponseHeaders(), //这里是字符串,axios内部对其进行了格式化变成了对象
                        status: xhr.status,
                        request: xhr, //xhr请求对象
                        statusText: xhr.statusText
                    })
                } else {
                    reject('请求失败' + xhr.status)
                }
            }

            //取消请求
            if (config.cancelToken) { //如果配置了cancelToken属性
                config.cancelToken.then((cancelInfo) => { //当调用了cancelToken实例上的Promise的执行器时,才会执行.then的方法参数,在其中进行取消xhr请求
                    xhr.abort();
                    reject('请求取消');
                })
            }

        }
    })
}

Axios.prototype.get = function (config) {
    return this.request({
        method: "GET"
    });
}
Axios.prototype.post = function (config) {
    return this.request({
        method: "POST"
    });
}

//创建取消axios请求类
Axios.prototype.cancelToken = function (executor) {
    //为当前cancelToken类添加属性
    this.promise = new Promise(executor)

}

//创建axios实例
function createInstance(config) {
    let context = new Axios(config);

    //创建请求函数,目的是能够直接x()发送请求,而返回的实例做不到
    let instance = Axios.prototype.request.bind(context); //若实例调用,则将方法中的this改变为实例
    //添加其他请求方法到instance函数上
    Object.keys(Axios.prototype).forEach(key => {
        instance[key] = Axios.prototype[key].bind(context); //将this指向为实例
    })

    //为请求函数添加实例上的属性和方法
    Object.keys(context).forEach(key => {
        instance[key] = context[key];
    })

    return instance;
}




let axios = createInstance({
    method: 'GET'
});


//拦截器调用
axios.interceptors.request.use(function (config) {
    console.log('req1')
    return config
}, function (err) {
    return Promise.reject(err);
})

axios.interceptors.request.use(function (config) {
    console.log('req2')
    return config
}, function (err) {
    console.log('err');
    return Promise.reject(err);
})

axios.interceptors.response.use(function (data) {
    console.log('resp1')
    return data;
}, function (err) {
    return Promise.reject(err);
})

axios.interceptors.response.use(function (data) {
    console.log('resp2')
    return data;
}, function (err) {
    return Promise.reject(err);
})

console.dir(axios);

let token;

axios({ //相当于调用Axios.prototype.request方法
    method: "GET",
    url: " http://localhost:3001/posts",
    cancelToken: new Promise(resolve => {
        token = resolve
    })
}).then(res => {
    console.log(res);
})

token();
